// Copyright 2022 jeff.li. and/or its affiliates.

#pragma once

#include <algorithm>

#include <tbir/runtime/container/string.h>
#include <tbir/runtime/container/string_view.h>
#include <tbir/runtime/container/unicode.h>
#include <tbir/runtime/file_reader.h>
#include <tbir/runtime/object.h>

namespace tbir::runtime {

    using PtrFileReader = std::shared_ptr<FileReader>;

    /*! \brief file node content in file */
    class FileNode : public Object {
    public:
        static constexpr const uint32_t _type_index = TypeIndex::kRuntimeFile;
        static constexpr const char *_type_key = "runtime.File";
        TBIR_DECLARE_FINAL_OBJECT_INFO(FileNode, Object);

        FileNode(const String &path, const String &mode = "r", const String &encoding = "utf-8")
                : path_(path), preader_(new FileReader(path, /* keep_newline */ true)) {
            // mode_ and _encoding are stored only for debug, they will not be checked while reading lines.
            mode_ = mode;
            std::transform(mode_.begin(), mode_.end(), mode_.begin(), ::tolower);
            MXCHECK(mode_ == "r" || mode_ == "rb") << "By now we only support \"r\" and \"rb\" mode.";
            encoding_ = encoding;
            std::transform(encoding_.begin(), encoding_.end(), encoding_.begin(), ::tolower);
            if (encoding_ == "utf8") {
                encoding_ = "utf-8";
            }
            MXCHECK(encoding_ == "utf-8") << "By now we only support \"utf-8\" encoding.";

            if (mode == "r") {
                readable_ = true;
            } else if (mode == "rb") {
                readable_ = true;
                binary_ = true;
            }
        }

        std::string GetRepr() const {
            std::ostringstream oss;
            oss << "File(\"" << path_ << "\", " << mode_ << ", " << encoding_ << ")";
            return oss.str();
        }

    public:
        bool HasNext() const;

        string_view path() const;

        String ReadString(int64_t size = -1) const;

        Unicode ReadUnicode(int64_t size = -1) const;

        RTValue Read(int64_t size = -1) const;

        String ReadLineString() const;

        Unicode ReadLineUnicode() const;

        List ReadLines() const;

        RTValue Next() const;

        RTValue Next(bool *has_next) const;

        RTView NextView(bool *has_next, RTValue *holder_or_null) const;

        void Close();

    private:
        // by now, we only support reader
        PtrFileReader preader_ = nullptr;
        String path_;
        String mode_;
        String encoding_;
        bool readable_ = false;
        bool binary_ = false;

        // Reference class
        friend class File;

        friend struct FileObjTrait;
    };

}  // namespace tbir::runtime
